package com.hhy.lombok.utils;

import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;

import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

import static com.hhy.lombok.constant.Constant.*;
public class ProcessUtils {



    public static String generateCallerVarName(int index) {
        return CALLER_VAR_NAME_PREFIX + index;
    }

    public static String generateProxyObjVarName(int index) {
        return PROXY_OBJ_VAR_NAME_PREFIX + index;
    }

    /**
     * 克隆一个字段的语法树节点，该节点作为方法的参数
     * 具有位置信息的语法树节点是不能复用的！
     *
     * @param treeMaker           语法树节点构造器
     * @param prototypeJCVariable 字段的语法树节点
     * @return 方法参数的语法树节点
     */
    public static JCTree.JCVariableDecl cloneJCVariableAsParam(TreeMaker treeMaker, JCTree.JCVariableDecl prototypeJCVariable) {
        return treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER), //访问标志。极其坑爹！！！
                prototypeJCVariable.name, //名字
                prototypeJCVariable.vartype, //类型
                null //初始化语句
        );
    }

    /**
     * 判断是否为set方法
     *
     * @param jcTree 语法树节点
     * @return 判断是否是Set方法
     */
    private static boolean isSetJCMethod(JCTree jcTree) {
        if (jcTree.getKind().equals(JCTree.Kind.METHOD)) {
            JCTree.JCMethodDecl jcMethod = (JCTree.JCMethodDecl) jcTree;
            return jcMethod.name.toString().startsWith(SET)
                    && jcMethod.params.size() == 1
                    && !jcMethod.mods.getFlags().contains(Modifier.STATIC);
        }
        return false;
    }

    /**
     * 提取出所有set方法的语法树节点
     *
     * @param jcClass 类的语法树节点
     * @return set方法的语法树节点的集合
     */
    static List<JCTree.JCMethodDecl> getSetJCMethods(JCTree.JCClassDecl jcClass) {
        ListBuffer<JCTree.JCMethodDecl> setJCMethods = new ListBuffer<>();

        //遍历jcClass的所有内部节点，可能是字段，方法等等
        for (JCTree jcTree : jcClass.defs) {
            //找出所有set方法节点，并添加
            if (isSetJCMethod(jcTree)) {
                //注意这个com.sun.tools.javac.util.List的用法，不支持链式操作，更改后必须赋值
                setJCMethods.append((JCTree.JCMethodDecl) jcTree);
            }
        }

        return setJCMethods.toList();
    }


    /**
     * 是否存在指定字段的set方法
     *
     * @param field  字段的语法树节点
     * @param jClass 类的语法树节点
     * @return 是否存在
     */
    public static boolean hasSetMethod(JCTree.JCVariableDecl field, JCTree.JCClassDecl jClass) {
        String setterName = toSetterMethodName(field.name.toString());
        for (JCTree jcTree : jClass.defs) {
            if (jcTree.getKind().equals(JCTree.Kind.METHOD)) {
                JCTree.JCMethodDecl jcMethod = (JCTree.JCMethodDecl) jcTree;
                if (setterName.equals(jcMethod.name.toString())
                        && jcMethod.params.size() == 1
                        && jcMethod.params.get(0).vartype.type.equals(field.vartype.type)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断是否存在指定字段的get方法，返回类型不作为判断依据，因为Java中方法重载与返回类型无关
     *
     * @param jcVariable 字段的语法树节点
     * @param jcClass    类的语法树节点
     * @return 是否存在
     */
    public static boolean hasGetterMethod(JCTree.JCVariableDecl jcVariable, JCTree.JCClassDecl jcClass) {
        String getMethodName = toGetterMethodName(jcVariable.name.toString());
        for (JCTree jcTree : jcClass.defs) {
            if (jcTree.getKind().equals(JCTree.Kind.METHOD)) {
                JCTree.JCMethodDecl jcMethod = (JCTree.JCMethodDecl) jcTree;
                if (getMethodName.equals(jcMethod.name.toString())
                        && jcMethod.params.size() == 0) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 字段名转换为set方法名
     */
    public static String toSetterMethodName(String fieldName) {
        return SET + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    /**
     * 字段名转换为get方法名
     */
    public static String toGetterMethodName(String fieldName) {
        return GET + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    public static Object getAnnotationValue(Element element, Class<?> clazz, String valName) {
        for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
            DeclaredType annotationType = annotationMirror.getAnnotationType();
            if (!clazz.getName().equals(annotationType.toString())) {
                continue;
            }
            for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
                AnnotationValue aValue = entry.getValue();
                if (aValue instanceof Attribute.Array) {
                    Attribute.Array arr = (Attribute.Array) aValue;
                    List<Attribute> values = arr.getValue();

                    ArrayList<Object> result = new ArrayList<>();
                    for (Attribute value : values) {
                        result.add(value.getValue());
                    }
                    return result;
                   // return values.stream().map(item -> item.getValue().toString()).collect(Collectors.toList());
                } else {
                    Attribute attr = (Attribute) aValue;
                    return attr.getValue().toString();
                }
            }

//            annotationMirror.getElementValues().entrySet().stream()
//                    .filter(e -> valName.equals(e.getKey().getSimpleName().toString()))
//                    .map(e -> {
//                        AnnotationValue aValue = e.getValue();
//                        Object attValue = aValue.getValue();
//                        if (attValue instanceof List) {
//                            List<Attribute> values = (List) attValue;
//                            return values.stream().map(item -> item.getValue().toString()).collect(Collectors.toList());
//                        } else {
//                            Attribute attr = (Attribute) attValue;
//                            return attr.getValue().toString();
//                        }
//                    });
        }
        return null;
    }
}